import 'dart:async';
import 'dart:io';
import 'dart:math';

import 'package:easy_device_info/easy_device_info.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:sgy_flutter/ble/base_service.dart';
import 'package:sgy_flutter/ble/ble_manager.dart';
import 'package:sgy_flutter/ble/right_ble_manager.dart';
import 'package:sgy_flutter/model/device_model.dart';
import 'package:sgy_flutter/model/device_type.dart';

import '../widget/app_dialog.dart';
import 'relay_stream_controller.dart';
import 'package:loader_overlay/loader_overlay.dart';

class BaseBleManager extends GetxService {
  final RelayStreamController<bool> deviceReady =
      RelayStreamController(value: false);

  final RelayStreamController<BluetoothConnectionState> connectionState =
      RelayStreamController(value: BluetoothConnectionState.disconnected);

  Rx<bool> isReady = Rx(false);

  BluetoothDevice? device;

  RxList<DeviceModel> scanResults = RxList.empty(growable: true);

  Stream<OnConnectionStateChangedEvent> get onConnectionStateChanged =>
      FlutterBluePlus.events.onConnectionStateChanged;

  List<BaseService> baseServices = List.empty(growable: true);

  DeviceModel? deviceModel;

  StreamSubscription? _subscriptionScanResult;
  StreamSubscription? _subscriptionOnConnectionStateChangedEvent;

  /// DFU模式,不弹出连接断开的弹窗
  bool dfuMode = false;

  Future<BaseBleManager> init() async {
    print('$runtimeType ready!');
    _subscriptionScanResult =
        FlutterBluePlus.scanResults.distinct().listen(_handleScanResults);
    _subscriptionOnConnectionStateChangedEvent = FlutterBluePlus
        .events.onConnectionStateChanged
        .listen(_handleOnConnectionStateChanged);
    return this;
  }

  @override
  void onClose() {
    _subscriptionOnConnectionStateChangedEvent?.cancel();
    _subscriptionScanResult?.cancel();
    super.onClose();
  }

  void _handleOnConnectionStateChanged(
      OnConnectionStateChangedEvent event) async {
    if (event.device.remoteId.str == deviceModel?.address) {
      if (event.connectionState == BluetoothConnectionState.connected) {
        isReady(true);
        deviceReady.add(true);
      }
      if (event.connectionState == BluetoothConnectionState.disconnected) {
        await cleanService();
        isReady(false);
        deviceReady.add(false);
      }
      connectionState.add(event.connectionState);
    }
    if (event.connectionState == BluetoothConnectionState.disconnected) {
      printError(info: Get.currentRoute);
      printError(
          info:
              "${Get.currentRoute} ${this.deviceModel?.name},${event.device.platformName}:disconnectReason:${event.device.disconnectReason},dfuMode:$dfuMode");
      final showLoaderOverlay =
          Get.currentRoute != "/DetailPage" && Get.currentRoute != "/HomePage";
      if (event.device.remoteId.str == deviceModel?.address &&
          !dfuMode &&
          Get.currentRoute != "/ScanPage" &&
          Get.currentRoute != "/HomePage" &&
          Get.currentRoute != "/DfuPage" &&
          Get.isDialogOpen == false) {
        if (_userDisconnect){
          _userDisconnect = false;
          return;
        }
        final bool? reconnect = await Get.dialog(
            AppDialog(
              title: deviceModel?.isDoubleSlide == true
                  ? "单侧设备已断开".tr
                  : "设备已断开".tr,
              message: "1.设备是否已转动唤醒。\n2.设备是否有电。\n3.功率计请不要在充电状态下连接APP。".tr,
              submit: "重新连接".tr,
              cancel: "取消".tr,
              onSubmit: () => Get.backLegacy(result: true),
              onCancel: () {
                Get.backLegacy();
              },
            ),
            name: "reconnectDialog",
            barrierDismissible: false);
        if (reconnect == true && deviceModel != null) {
          if (showLoaderOverlay) {
            Get.context?.loaderOverlay.show(progress: "连接中".tr);
          }
          try {
            printError(
                info:
                    "runtimeType:${this.runtimeType},${runtimeType == BleManager},${runtimeType == RightBleManager}");
            await _reconnect();
            if (deviceModel?.isDoubleSlide == true) {
              if (runtimeType == RightBleManager) {
                BleManager m = Get.find();
                await m._reconnect();
              } else {
                RightBleManager m = Get.find();
                await m._reconnect();
              }
            }
          } catch (e) {
          } finally {
            if (showLoaderOverlay) {
              Get.context?.loaderOverlay.hide();
            }
          }
        }
      }
    }
  }

  Future _reconnect() async {
    if (deviceModel != null) {
      await connect(deviceModel!);
    }
  }

  Future<bool> ensureDeviceConnected() async {
    if (connectionState.value == BluetoothConnectionState.disconnected) {
      Get.context?.loaderOverlay.hide();
      final bool? reconnect = await Get.dialog(
          AppDialog(
            title: "设备已断开".tr,
            message: "1.设备是否已转动唤醒。\n2.设备是否有电。\n3.功率计请不要在充电状态下连接APP。".tr,
            submit: "重新连接".tr,
            cancel: "取消".tr,
            onSubmit: () => Get.backLegacy(result: true),
          ),
          barrierDismissible: false);
      if (reconnect == true) {
        if (deviceModel != null) {
          try {
            Get.context?.loaderOverlay.show(progress: "连接中".tr);
            await connect(deviceModel!);
            Get.context?.loaderOverlay.hide();
          } catch (e) {
            printError(info: "ensureDeviceConnected catch:$e");
            await Future.delayed(Duration(milliseconds: 50));
            Get.context?.loaderOverlay.hide();
            return await ensureDeviceConnected();
          }
          return true;
        } else {
          return false;
        }
      } else {
        return false;
      }
    } else {
      return true;
    }
  }

  var _lastHandleScanResultsTime = DateTime.timestamp();

  void _handleScanResults(List<ScanResult> scanResultsList) {
    final duration =
        DateTime.timestamp().difference(_lastHandleScanResultsTime);
    // print("duration.inSeconds:${duration.inSeconds}");
    if (duration.inSeconds < 1) {
      return;
    }
    final value = scanResultsList.where((test) {
      for (var type in DeviceType.values) {
        if (test.device.platformName.startsWith(type.prefix)) {
          return true;
        }
      }
      return false;
    }).toList();
    _lastHandleScanResultsTime = DateTime.timestamp();
    final scanList =
        value.where((test) => test.device.platformName.isNotEmpty).toList();
    List<DeviceModel> list = List.empty(growable: true);
    for (var index = 0; index < scanResults.length; index++) {
      final model = scanResults[index];
      final i = scanList
          .indexWhere((test) => test.device.remoteId.str == model.address);
      if (i != -1) {
        list.add(DeviceModel.fromScanResult(scanList[i]));
      }
    }
    for (var index = 0; index < scanList.length; index++) {
      final model = scanList[index];
      final i = scanResults
          .indexWhere((test) => test.address == model.device.remoteId.str);
      if (i == -1) {
        list.add(DeviceModel.fromScanResult(model));
      }
    }

    scanResults.clear();
    scanResults.addAll(list);
  }

  Future startScan() async {
    if (Platform.isIOS) {
      printInfo(
          info:
              "DeviceInfoService.info.version:${DeviceInfoService.info.deviceOsVersion}");
      var status =
          DeviceInfoService.info.deviceOsVersion?.startsWith("13.0") == true
              ? await Permission.bluetooth.request()
              : await Permission.bluetooth.status;
      printInfo(info: "startScan status:$status");
      if (status.isDenied) {
        final canRequest = await Get.dialog(
            AppDialog(
              title: "提示".tr,
              message: "请允许APP要求的权限，否则将无法正常使用。".tr,
              submit: "好的，我知道了！".tr,
              onSubmit: () {
                Get.backLegacy(result: true);
              },
            ),
            barrierDismissible: false);
        if (canRequest == true) {
          status = await Permission.bluetooth.request();
        }
      }
      if (status.isGranted) {
        await _startScan();
      } else {
        await _startScan();
      }
    } else {
      printInfo(
          info:
              "DeviceInfoService.info.version:${DeviceInfoService.info.androidSdkInt}");
      final beforeAndroid12 = (DeviceInfoService.info.androidSdkInt ?? 0) < 31;
      var status = beforeAndroid12
          ? await Permission.bluetooth.status
          : await Permission.bluetoothScan.status;
      printInfo(info: "startScan status:$status");
      if (beforeAndroid12){
        status = await Permission.location.status;
      }
      if (status.isDenied) {
        final canRequest = await Get.dialog(
            AppDialog(
              title: "提示".tr,
              message: "请允许APP要求的权限，否则将无法正常使用。".tr,
              submit: "好的，我知道了！".tr,
              onSubmit: () {
                Get.backLegacy(result: true);
              },
            ),
            barrierDismissible: false);
        if (canRequest == true) {
          status = beforeAndroid12
              ? await Permission.location.request()
              : await Permission.bluetoothScan.request();
        }
      }
      if (status.isGranted) {
        await _startScan();
      }
    }
  }

  Future _startScan()async{
    if (await FlutterBluePlus.adapterState.first != BluetoothAdapterState.on){
      await Get.dialog(AppDialog(title: "提示".tr, message: "请先打开蓝牙".tr, submit: "确定".tr), barrierDismissible: false);
    }else {
      if (await FlutterBluePlus.isSupported) {
        if (!FlutterBluePlus.isScanningNow) {
          await FlutterBluePlus.startScan(
              removeIfGone: Duration(seconds: 5), continuousUpdates: true);
        }
      }
    }

  }

  Future stopScan() async {
    if (FlutterBluePlus.isScanningNow) {
      await FlutterBluePlus.stopScan();
    }
    printInfo(info: "stopScan status:${FlutterBluePlus.isScanningNow}");
  }

  Future connect(DeviceModel model) async {
    deviceModel = model;
    if (Platform.isAndroid) {
      var status = await Permission.bluetoothConnect.status;
      print("status:$status");
      if (status.isDenied) {
        final canRequest = await Get.dialog(
            AppDialog(
              title: "提示".tr,
              message: "请允许APP要求的权限，否则将无法正常使用。".tr,
              submit: "好的，我知道了！".tr,
              onSubmit: () {
                Get.backLegacy(result: true);
              },
            ),
            barrierDismissible: false);
        if (canRequest == true) {
          status = await Permission.bluetoothConnect.request();
        }
      }
      if (status.isGranted) {
        final device = _buildDevice(model);
        await _connect(device);
      }
    } else {
      final device = _buildDevice(model);
      await _connect(device);
    }
  }

  BluetoothDevice _buildDevice(DeviceModel model) {
    BluetoothDevice? device = FlutterBluePlus.connectedDevices
        .firstWhereOrNull((test) => test.remoteId.str == model.address);
    if (device != null) {
      printInfo(info: "_buildDevice connectedDevices:${model}");
      return device;
    } else {
      device = FlutterBluePlus.lastScanResults
          .firstWhereOrNull((test) => test.device.remoteId.str == model.address)
          ?.device;
      if (device != null) {
        printInfo(info: "_buildDevice lastScanResults:${model}");
        return device;
      } else {
        printInfo(info: "_buildDevice remoteId:${model}");
        device = BluetoothDevice(remoteId: DeviceIdentifier(model.address));
        return device;
      }
    }
  }

  Future _connect(BluetoothDevice device) async {
    printInfo(info: "_connect:${device},${device.isConnected},${this.device}");
    if (this.device?.remoteId == device.remoteId) {
      if (connectionState.value == BluetoothConnectionState.connected) {
        return;
      }
    }

    this.device = device;
    beforeConnect();
    connectionState.add(BluetoothConnectionState.connecting);

    // var subscription = device.connectionState
    //     .skip(1)
    //     .listen((BluetoothConnectionState state) async {
    //   printError(info: "_connect:connectionState:$state");
    //
    //   if (state == BluetoothConnectionState.disconnected) {
    //     deviceReady.add(false);
    //   }
    //   connectionState.add(state);
    // });
    // device.cancelWhenDisconnected(subscription, delayed: true, next: true);
    // await connectionState.stream.where((val) => val == BluetoothConnectionState.disconnected).first;
    // connectionState(BluetoothConnectionState.connecting);
    await device.connect(timeout: Duration(seconds: 10));
    final services = await device.discoverServices();
    List<StreamSubscription> subscriptions = await initializeService(services);
    for (var subscription in subscriptions) {
      device.cancelWhenDisconnected(subscription);
    }
    deviceReady.add(true);
    onDeviceConnected();
  }

  Future disconnectLast(DeviceModel? nextModel) async {
    if (deviceModel != null && deviceModel?.address != nextModel?.address) {
      await disconnect(deviceModel!);
    }
  }

  bool _userDisconnect = false;

  /**
   * 用户断开，有回调没有弹窗
   */
  Future userDisconnect(DeviceModel model)async{
    _userDisconnect = true;
    await _disconnect(model);
  }

  Future _disconnect(DeviceModel model)async{
    connectionState.add(BluetoothConnectionState.disconnecting);
    BluetoothDevice device = _buildDevice(model);
    if (device.isConnected) {
      await device.disconnect();
      try{
        await connectionState.stream
            .firstWhere((test) => test == BluetoothConnectionState.disconnected)
            .timeout(Duration(seconds: 2));
      }catch(e){
        printError(info: "$e");
      }
    } else if (device.isDisconnected) {
      connectionState.add(BluetoothConnectionState.disconnected);
    }
  }
  /**
   * 断开并清理状态，不会有disconnected的回调
   */
  Future disconnect(DeviceModel model) async {
    printInfo(info: "disconnect:$model");
    this.device = null;
    this.deviceModel = null;
    await _disconnect(model);
  }

  /**
   * 初始化服务
   */
  Future<List<StreamSubscription>> initializeService(
      List<BluetoothService> services) async {
    List<StreamSubscription> list = List.empty(growable: true);
    for (var service in baseServices) {
      var subscriptions = await service.initializeService(services);
      list.addAll(subscriptions);
    }
    return list;
  }

  /**
   * 清理服务
   */
  Future cleanService() async {
    for (var service in baseServices) {
      await service.cleanup();
    }
  }

  FutureOr onDeviceConnected() {}

  void beforeConnect() {}
}

// split write should be used with caution.
//    1. due to splitting, `characteristic.read()` will return partial data.
//    2. it can only be used *with* response to avoid data loss
//    3. The characteristic must be designed to support split data
extension SplitWrite on BluetoothCharacteristic {
  Future<void> splitWrite(List<int> value, {int timeout = 15}) async {
    int chunk =
        min(device.mtuNow - 3, 512); // 3 bytes BLE overhead, 512 bytes max
    for (int i = 0; i < value.length; i += chunk) {
      List<int> subvalue = value.sublist(i, min(i + chunk, value.length));
      await write(subvalue, withoutResponse: false, timeout: timeout);
    }
  }
}
